﻿//
// Author: rgy
// Date: 2016-03-18 09:23:00
//

using UnityEngine;
using System.Globalization;
using System.Text;
using UnityEngine.UI;

namespace SoarD.Tween
{
    enum MoveType
    {
        Anchored,       //UI
        Position,       //世界坐标
        LocalPosition,  //区域坐标
        LocalPositionLookAt,    //区域坐标并lookAt
        Scale,
        Rotate,
        LocalRotate,
        ImageColor,
        RectTransSizeDelta,
        SpriteRendererColor,

        SliderMove,
        SubtendSliderMove,
        ImageFillAmount,
    }
    /// <summary>
    /// 自定义移动
    /// </summary>
    internal sealed class TweenFuncPosObj : TweenObjectBase
    {
        private RectTransform _rectTransform;
        private CanvasGroup _canvasGroup;
        private float _startY;
        private float _targetY;
        private float _time;
        private SoarDTween.PositionDelegate _posResult;
        private SoarDTween.AlphaDelegate _alphaResult;

        public static TweenFuncPosObj Create(RectTransform rectTransform, float targetY, float time, SoarDTween.PositionDelegate posResult, SoarDTween.AlphaDelegate alphaResult)
        {
            var tweenObj = new TweenFuncPosObj
            {
                gameObject = rectTransform.gameObject,
                _rectTransform = rectTransform,
                _canvasGroup = rectTransform.GetComponent<CanvasGroup>(),
                _startY = rectTransform.anchoredPosition.y,
                _targetY = targetY,
                _dt = 0,
                _time = time,
                _posResult = posResult,
                _alphaResult = alphaResult,
            };
            return tweenObj;
        }

        public override bool Excute(float dt)
        {
            _dt += dt;
            float y = Mathf.Lerp(_startY, _targetY, _dt / _time);
            _canvasGroup.alpha = _alphaResult(y);
            _rectTransform.anchoredPosition = _posResult(y);
            return _dt >= _time;
        }
    }

    public sealed class TweenRtPosObj : TweenObjectBase
    {
        private RectTransform _rectTransform;
        private Transform _transform;
        private Vector3 _startV3;
        private Vector3 _targetV3;
        #region Color
        private SpriteRenderer _spriteRenderer;
        private Graphic _graphic;
        private Color _startColor;
        private Color _targetColor;
        #endregion
        private float _time;
        private MoveType _moveType;

        public static TweenRtPosObj CreateMove(RectTransform rectTransform, Vector2 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = rectTransform.gameObject;
            tweenObj._rectTransform = rectTransform;
            tweenObj._startV3 = rectTransform.anchoredPosition;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.Anchored;
            return tweenObj;
        }

        public static TweenRtPosObj CreateMove(GameObject gameObject, Vector3 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = gameObject;
            tweenObj._transform = gameObject.transform;
            tweenObj._startV3 = gameObject.transform.localPosition;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.Position;
            return tweenObj;
        }

        public static TweenRtPosObj CreateMoveLocal(GameObject gameObject, Vector3 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = gameObject;
            tweenObj._transform = gameObject.transform;
            tweenObj._startV3 = gameObject.transform.localPosition;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.LocalPosition;
            return tweenObj;
        }

        public static TweenRtPosObj CreateMoveLocalLookAt(GameObject gameObject, Vector3 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = gameObject;
            tweenObj._transform = gameObject.transform;
            tweenObj._startV3 = gameObject.transform.localPosition;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.LocalPositionLookAt;
            return tweenObj;
        }

        public static TweenRtPosObj CreateScale(Transform transform, Vector3 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = transform.gameObject;
            tweenObj._transform = transform;
            tweenObj._startV3 = transform.localScale;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.Scale;
            return tweenObj;
        }

        public static TweenRtPosObj CreateRotate(Transform transform, Vector3 targetVal, float time, bool minAngle)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = transform.gameObject;
            tweenObj._transform = transform;
            tweenObj._startV3 = transform.eulerAngles;

            if (minAngle)
            {
                Vector3 start = tweenObj._startV3;
                tweenObj._targetV3 = new Vector3(SoarDTween.closestRot(start.x, targetVal.x), SoarDTween.closestRot(start.y, targetVal.y), SoarDTween.closestRot(start.z, targetVal.z));
            }
            else
            {
                tweenObj._targetV3 = targetVal;
            }

            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.Rotate;
            return tweenObj;
        }

        public static TweenRtPosObj CreateRotateLocal(Transform transform, Vector3 targetVal, float time, bool minAngle)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = transform.gameObject;
            tweenObj._transform = transform;
            tweenObj._startV3 = transform.localEulerAngles;

            if (minAngle)
            {
                Vector3 start = tweenObj._startV3;
                tweenObj._targetV3 = new Vector3(SoarDTween.closestRot(start.x, targetVal.x), SoarDTween.closestRot(start.y, targetVal.y), SoarDTween.closestRot(start.z, targetVal.z));
            }
            else
            {
                tweenObj._targetV3 = targetVal;
            }

            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.LocalRotate;
            return tweenObj;
        }

        public static TweenRtPosObj CreateColor(Graphic graphic, Color targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = graphic.gameObject;
            tweenObj._graphic = graphic;
            tweenObj._startColor = graphic.color;
            tweenObj._targetColor = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.ImageColor;
            return tweenObj;
        }

        public static TweenRtPosObj CreateSizeDelta(RectTransform rt, Vector2 targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = rt.gameObject;
            tweenObj._rectTransform = rt;
            tweenObj._startV3 = rt.sizeDelta;
            tweenObj._targetV3 = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.RectTransSizeDelta;
            return tweenObj;
        }

        public static TweenRtPosObj CreateSizeDeltaX(RectTransform rt, float x, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = rt.gameObject;
            tweenObj._rectTransform = rt;
            tweenObj._startV3 = rt.sizeDelta;
            tweenObj._targetV3 = new Vector3(x, rt.sizeDelta.y, 0);
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.RectTransSizeDelta;
            return tweenObj;
        }
        public static TweenRtPosObj CreateSizeDeltaY(RectTransform rt, float y, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = rt.gameObject;
            tweenObj._rectTransform = rt;
            tweenObj._startV3 = rt.sizeDelta;
            tweenObj._targetV3 = new Vector3(rt.sizeDelta.x, y, 0);
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.RectTransSizeDelta;
            return tweenObj;
        }

        public static TweenRtPosObj CreateColor(SpriteRenderer spriteRenderer, Color targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenRtPosPool.Create();
            tweenObj.gameObject = spriteRenderer.gameObject;
            tweenObj._spriteRenderer = spriteRenderer;
            tweenObj._startColor = spriteRenderer.color;
            tweenObj._targetColor = targetVal;
            tweenObj._dt = 0;
            tweenObj._time = time;
            tweenObj._moveType = MoveType.SpriteRendererColor;
            return tweenObj;
        }

        public override bool Excute(float dt)
        {
            _dt += dt;
            switch (_moveType)
            {
                case MoveType.Anchored:
                    _rectTransform.anchoredPosition = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.Position:
                    _transform.position = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.LocalPosition:
                    _transform.localPosition = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.LocalPositionLookAt:
                    if (_dt < _time)
                    {
                        _transform.LookAt(SoarDMath.Lerp(_startV3, _targetV3, _dt + 0.01f, _time));
                    }
                    _transform.localPosition = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.Scale:
                    _transform.localScale = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.Rotate:
                    _transform.eulerAngles = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.LocalRotate:
                    _transform.localEulerAngles = SoarDMath.Lerp(_startV3, _targetV3, _dt, _time);
                    break;
                case MoveType.ImageColor:
                    _graphic.color = SoarDMath.Lerp(_startColor, _targetColor, _dt, _time);
                    break;
                case MoveType.SpriteRendererColor:
                    _spriteRenderer.color = SoarDMath.Lerp(_startColor, _targetColor, _dt, _time);
                    break;
                case MoveType.RectTransSizeDelta:
                   _rectTransform.sizeDelta = Vector3.Lerp(_startV3, _targetV3, _dt / _time);
                    break;
            }
            return _dt >= _time;
        }

        public override void Destroy()
        {
            _transform = null;
            _rectTransform = null;
            _spriteRenderer = null;
            _graphic = null;
            SoarDTween.Instance.TweenRtPosPool.Recycle(this);
        }

        public override void DoPingPong()
        {
            if (_moveType == MoveType.ImageColor)
            {
                var tempColor = _startColor;
                _startColor = _targetColor;
                _targetColor = tempColor;
            }
            else
            {
                var tempV3 = _startV3;
                _startV3 = _targetV3;
                _targetV3 = tempV3;
            }

        }
    }

    /// <summary>
    /// 文本跳动
    /// </summary>
    internal sealed class TweenJumpObj : TweenObjectBase
    {
        private Text _text;
        private double _startVal;
        private double _targetVal;
        private float _time;

        public static TweenJumpObj Create(Text text, double startVal, double targetVal, float time)
        {
            TweenJumpObj obj = new TweenJumpObj
            {
                gameObject = text.gameObject,
                _text = text,
                _startVal = startVal,
                _targetVal = targetVal,
                _time = time,
                _dt = 0,
            };
            return obj;
        }

        public override bool Excute(float dt)
        {
            _dt += dt;
            long value = (long)SoarDMath.Lerp(_startVal, _targetVal, _dt / _time);
            _text.text = GetJumpVal(value.ToString(CultureInfo.InvariantCulture));
            return _dt >= _time;
        }

        public string GetJumpVal(string str)
        {
            StringBuilder sb = new StringBuilder();
            for (int t = 1, i = str.Length - 1; i >= 0; i--, t++)
            {
                sb.Insert(0, str[i]);
                if (t % 3 == 0 && i > 0)
                {
                    sb.Insert(0, ",");
                }
            }
            return sb.ToString();
        }
    }

    /// <summary>
    /// Slider滑动(向目标)
    /// </summary>
    public sealed class TweenSliderObj : TweenObjectBase
    {
        private Slider _slider;
        private Image _image;
        private float _startVal;
        private float _targetVal;
        private float _time;
        private MoveType _moveType;
        public static TweenSliderObj Create(Slider slider, float targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenSliderPool.Create();
            tweenObj.gameObject = slider.gameObject;
            tweenObj._slider = slider;
            tweenObj._startVal = slider.value;
            tweenObj._targetVal = targetVal;
            tweenObj._time = time;
            tweenObj._dt = 0;
            tweenObj._moveType = MoveType.SliderMove;
            return tweenObj;
        }
        public static TweenSliderObj Create(Image image, float targetVal, float time)
        {
            var tweenObj = SoarDTween.Instance.TweenSliderPool.Create();
            tweenObj.gameObject = image.gameObject;
            tweenObj._image = image;
            tweenObj._startVal = image.fillAmount;
            tweenObj._targetVal = targetVal;
            tweenObj._time = time;
            tweenObj._dt = 0;
            tweenObj._moveType = MoveType.ImageFillAmount;
            return tweenObj;
        }
        public override bool Excute(float dt)
        {
            _dt += dt;
            switch (_moveType)
            {
                case MoveType.SliderMove:
                    _slider.value = Mathf.Lerp(_startVal, _targetVal, _dt / _time);
                    break;
                case MoveType.ImageFillAmount:
                    _image.fillAmount = Mathf.Lerp(_startVal, _targetVal, _dt / _time);
                    break;
            }
            return _dt >= _time;
        }

        public override void Destroy()
        {
            gameObject = null;
            _slider = null;
            _image = null;
            SoarDTween.Instance.TweenSliderPool.Recycle(this);
        }
    }

    /// <summary>
    /// Slider滑动(向终点值)
    /// </summary>
    internal sealed class TweenSliderDirectObj : TweenObjectBase
    {
        private Slider _slider;
        private float _startVal;
        private float _targetVal;
        private float _time;
        private float _remainTime;
        private float _curTargetVal;
        private bool _direct;
        private bool _twice;
        public static TweenSliderDirectObj Create(Slider slider, float targetVal, float time, bool direct)
        {
            TweenSliderDirectObj obj = new TweenSliderDirectObj
            {
                gameObject = slider.gameObject,
                _slider = slider,
                _startVal = slider.value,
                _targetVal = targetVal,
                _dt = 0,
                _direct = direct,
                _twice = direct ? targetVal < slider.value : targetVal > slider.value,
            };
            if (obj._twice)
            {
                if (direct)
                {
                    float dis = targetVal + 1 - obj._startVal;
                    obj._time = (1 - obj._startVal) / dis * time;
                }
                else
                {
                    float dis = obj._startVal + 1 - targetVal;
                    obj._time = obj._startVal / dis * time;
                }
                obj._curTargetVal = direct ? 1 : 0;
            }
            else
            {
                obj._time = time;
                obj._curTargetVal = targetVal;
            }
            obj._remainTime = time - obj._time;
            return obj;
        }
        public override bool Excute(float dt)
        {
            _dt += dt;
            float val = Mathf.Lerp(_startVal, _curTargetVal, _dt / _time);
            _slider.value = val;
            if (_twice && _dt >= _time)
            {
                _startVal = _direct ? 0 : 1;
                _time = _remainTime;
                _remainTime = 0;
                _curTargetVal = _targetVal;
                _dt = 0;
                _twice = false;
            }
            return _dt >= _time;
        }
    }

    internal sealed class TweenCrystalObj : TweenObjectBase
    {
        private Transform _transform;
        private float _rotateY;
        private float _startPosY;
        private float _targetPosY;
        private float _rotateYSpeed;
        private float _time;

        public static TweenCrystalObj Create(Transform transform, float rotateYSpeed, float targetPosY, float time)
        {
            var tweenObj = new TweenCrystalObj
            {
                gameObject = transform.gameObject,
                _transform = transform,
                _rotateY = transform.localEulerAngles.y,
                _startPosY = transform.localPosition.y,
                _targetPosY = targetPosY,
                _rotateYSpeed = rotateYSpeed,
                _time = time,
            };
            return tweenObj;
        }

        public override bool Excute(float dt)
        {
            _dt += dt;
            _rotateY += _rotateYSpeed * dt;
            if (_rotateY > 180)
            {
                _rotateY -= 360;
            }
            else if (_rotateY < 180)
            {
                _rotateY += 360;
            }
            _transform.SetLocalPositionY(Mathf.Lerp(_startPosY, _targetPosY, _dt / _time));
            _transform.SetLocalEulerAngleY(_rotateY);
            if (_dt >= _time)
            {
                _dt = 0;
                var temp = _startPosY;
                _startPosY = _targetPosY;
                _targetPosY = temp;
            }
            return false;
        }
    }


    internal sealed class GoldRouletteObj : TweenObjectBase
    {
        private Transform _transform;
        private float _startRotate;
        private float _targetRotate;
        private float _speed;
        private float _addSpeed;
        private float _addTime;
        private float _normalTime;
        private float _findStart;
        private float _findTime;
        private float _time;
        private bool _inFind;

        public static GoldRouletteObj Create(Transform transform, float addSpeed, float targetRotate, float addTime, float normalTime, float _findTime)
        {
            var tweenObj = new GoldRouletteObj
            {
                gameObject = transform.gameObject,
                _transform = transform,
                _startRotate = transform.localEulerAngles.z,
                _targetRotate = targetRotate,
                _speed = 0,
                _addSpeed = addSpeed,
                _addTime = addTime,
                _normalTime = normalTime,
                _findTime = _findTime,
                _time = addTime + normalTime + _findTime,
                _inFind = false,
            };
            return tweenObj;
        }

        public override bool Excute(float dt)
        {
            _dt += dt;
            if (_dt <= _addTime)
            {
                _speed = Mathf.Lerp(0, _addSpeed, _dt / _addTime);
                _startRotate -= _speed * dt;
            }
            else if (_dt <= _addTime + _normalTime)
            {
                _startRotate -= _speed * dt;
            }
            else
            {
                if (!_inFind)
                {
                    _findStart = _startRotate;
                    while (_findStart < _targetRotate + 360)
                    {
                        _findStart += 360;
                    }
                    _inFind = true;
                }
                float ratioPassed = (_dt - _addTime - _normalTime) / _findTime;
                ratioPassed = ratioPassed > 1 ? 1 : ratioPassed;
                ratioPassed = ratioPassed < 0 ? 0 : ratioPassed;
                ratioPassed--;
                float end = _targetRotate - _findStart;
                _startRotate = end*(ratioPassed* ratioPassed* ratioPassed+1)+_findStart;
            }
            if (_startRotate > 180)
            {
                _startRotate -= 360;
            }
            else if (_startRotate < 180)
            {
                _startRotate += 360;
            }
            _transform.SetLocalEulerAngleZ(_startRotate);
            return _dt > _time;
        }
    }
}